# -*- coding: UTF-8 -*-
import bs
import random

defaultPowerupInterval = 8000

class PowerupMessage(object):
    """
    category: Message Classes

    Tell something to get a powerup.
    This message is normally recieved by touching
    a bs.Powerup box.
    
    Attributes:
    
       powerupType
          The type of powerup to be granted (a string).
          See bs.Powerup.powerupType for available type values.

       sourceNode
          The node the powerup game from, or an empty bs.Node ref otherwise.
          If a powerup is accepted, a bs.PowerupAcceptMessage should be sent
          back to the sourceNode to inform it of the fact. This will generally
          cause the powerup box to make a sound and disappear or whatnot.
    """
    def __init__(self,powerupType,sourceNode=bs.Node(None)):
        """
        Instantiate with given values.
        See bs.Powerup.powerupType for available type values.
        """
        self.powerupType = powerupType
        self.sourceNode = sourceNode

class PowerupAcceptMessage(object):
    """
    category: Message Classes

    Inform a bs.Powerup that it was accepted.
    This is generally sent in response to a bs.PowerupMessage
    to inform the box (or whoever granted it) that it can go away.
    """
    pass

class _TouchedMessage(object):
    pass

class PowerupFactory(object):
    """
    category: Game Flow Classes
    
    Wraps up media and other resources used by bs.Powerups.
    A single instance of this is shared between all powerups
    and can be retrieved via bs.Powerup.getFactory().

    Attributes:

       model
          The bs.Model of the powerup box.

       modelSimple
          A simpler bs.Model of the powerup box, for use in shadows, etc.

       texBox
          Triple-bomb powerup bs.Texture.

       texPunch
          Punch powerup bs.Texture.

       texIceBombs
          Ice bomb powerup bs.Texture.

       texStickyBombs
          Sticky bomb powerup bs.Texture.

       texShield
          Shield powerup bs.Texture.

       texImpactBombs
          Impact-bomb powerup bs.Texture.

       texHealth
          Health powerup bs.Texture.

       texLandMines
          Land-mine powerup bs.Texture.

       texCurse
          Curse powerup bs.Texture.

       healthPowerupSound
          bs.Sound played when a health powerup is accepted.

       powerupSound
          bs.Sound played when a powerup is accepted.

       powerdownSound
          bs.Sound that can be used when powerups wear off.

       powerupMaterial
          bs.Material applied to powerup boxes.

       powerupAcceptMaterial
          Powerups will send a bs.PowerupMessage to anything they touch
          that has this bs.Material applied.
    """

    def __init__(self):
        """
        Instantiate a PowerupFactory.
        You shouldn't need to do this; call bs.Powerup.getFactory()
        to get a shared instance.
        """

        self._lastPowerupType = None

        self.model = bs.getModel("powerup")
        self.modelSimple = bs.getModel("powerupSimple")
        self.droneModel = bs.getModel("shield")
        self.botModel = bs.getModel("egg")

        self.texBomb = bs.getTexture("powerupBomb")
        self.texPunch = bs.getTexture("powerupPunch")
        self.texIceBombs = bs.getTexture("powerupIceBombs")
        self.texStickyBombs = bs.getTexture("powerupStickyBombs")
        self.texShield = bs.getTexture("powerupShield")
        self.texImpactBombs = bs.getTexture("powerupImpactBombs")
        self.texHealth = bs.getTexture("powerupHealth")
        self.texLandMines = bs.getTexture("powerupLandMines")
        self.texCurse = bs.getTexture("powerupCurse")
        self.texTowerFighter = bs.getTexture("ouyaYButton")
        self.texSplitBombs = bs.getTexture("menuIcon")
        self.texTriggerBombs = bs.getTexture("settingsIcon")
        self.texClusterBombs = bs.getTexture("ouyaUButton")
        self.texDrone = bs.getTexture("black")
        self.texBot = bs.getTexture(random.choice(["eggTex1", "eggTex2", "eggTex3"]))

        # For SnowBall
        self.texSno = bs.getTexture("bunnyColor")
        self.snoModel = bs.getModel("frostyPelvis")

        self.healthPowerupSound = bs.getSound("healthPowerup")
        self.powerupSound = bs.getSound("powerup01")
        self.powerdownSound = bs.getSound("powerdown01")
        self.dropSound = bs.getSound("boxDrop")

        # material for powerups
        self.powerupMaterial = bs.Material()

        # material for anyone wanting to accept powerups
        self.powerupAcceptMaterial = bs.Material()

        # pass a powerup-touched message to applicable stuff
        self.powerupMaterial.addActions(
            conditions=(("theyHaveMaterial",self.powerupAcceptMaterial)),
            actions=(("modifyPartCollision","collide",True),
                     ("modifyPartCollision","physical",False),
                     ("message","ourNode","atConnect",_TouchedMessage())))

        # we dont wanna be picked up
        self.powerupMaterial.addActions(
            conditions=("theyHaveMaterial",
                        bs.getSharedObject('pickupMaterial')),
            actions=( ("modifyPartCollision","collide",False)))

        self.powerupMaterial.addActions(
            conditions=("theyHaveMaterial",
                        bs.getSharedObject('footingMaterial')),
            actions=(("impactSound",self.dropSound,0.5,0.1)))

        self._powerupDist = []
        for p,freq in getDefaultPowerupDistribution():
            for i in range(int(freq)):
                self._powerupDist.append(p)

    def getRandomPowerupType(self,forceType=None,excludeTypes=[]):
        """
        Returns a random powerup type (string).
        See bs.Powerup.powerupType for available type values.

        There are certain non-random aspects to this; a 'curse' powerup,
        for instance, is always followed by a 'health' powerup (to keep things
        interesting). Passing 'forceType' forces a given returned type while
        still properly interacting with the non-random aspects of the system
        (ie: forcing a 'curse' powerup will result
        in the next powerup being health).
        """
        if forceType:
            t = forceType
        else:
            # if the last one was a curse, make this one a health to
            # provide some hope
            if self._lastPowerupType == 'curse':
                t = 'health'
            else:
                while True:
                    t = self._powerupDist[
                        random.randint(0, len(self._powerupDist)-1)]
                    if t not in excludeTypes:
                        break
        self._lastPowerupType = t
        return t


def getDefaultPowerupDistribution():
    return (('tripleBombs',4),
            ('iceBombs',4),
            ('punch',100),
            ('impactBombs',4),
            ('landMines',3),
            ('stickyBombs',4),
            ('shield',1),
            ('health',2),
            ('curse',2),
            ('triggerBombs', 1),
            ('towerFighter', 10),
            ('splitBombs', 2),
            ('clusterBombs', 2),
            ('drone', 10),
            ('bot',100),
            ('snowball', 2))

class Powerup(bs.Actor):
    """
    category: Game Flow Classes

    A powerup box.
    This will deliver a bs.PowerupMessage to anything that touches it
    which has the bs.PowerupFactory.powerupAcceptMaterial applied.

    Attributes:

       powerupType
          The string powerup type.  This can be 'tripleBombs', 'punch',
          'iceBombs', 'impactBombs', 'landMines', 'stickyBombs', 'shield',
          'health', or 'curse'.

       node
          The 'prop' bs.Node representing this box.
    """

    def __init__(self,position=(0,1,0),powerupType='tripleBombs',expire=True):
        """
        Create a powerup-box of the requested type at the requested position.

        see bs.Powerup.powerupType for valid type strings.
        """
        
        bs.Actor.__init__(self)

        factory = self.getFactory()
        self.powerupType = powerupType;
        self._powersGiven = False

        cr = (1, 1, 1)
        txt = "Bomb"

        if powerupType == 'tripleBombs': tex = factory.texBomb; cr = (0.9, 0.9, 0.1); txt = "三重炸弹"
        elif powerupType == 'punch': tex = factory.texPunch; cr = (1, 0.1, 0.1); txt = "拳击手套"
        elif powerupType == 'iceBombs': tex = factory.texIceBombs; cr = (0.1, 0.2, 0.99); txt = "冰冻炸弹"
        elif powerupType == 'impactBombs': tex = factory.texImpactBombs; cr = (0.2, 0.2, 0.2); txt = "触感炸弹"
        elif powerupType == 'landMines': tex = factory.texLandMines; cr = (0.1, 0.6, 0.9); txt = "地雷"
        elif powerupType == 'stickyBombs': tex = factory.texStickyBombs; cr = (0.1, 0.8, 0.12); txt = "黏性炸弹"
        elif powerupType == 'shield': tex = factory.texShield; cr = (0.91, 0.1, 0.91); txt = "保护罩"
        elif powerupType == 'health': tex = factory.texHealth; cr = (1, 0, 0); txt = "救生包"
        elif powerupType == 'curse': tex = factory.texCurse; cr = (0.3, 0.3, 0.3); txt = "诅咒"
        elif powerupType == 'triggerBombs': tex = factory.texTriggerBombs; cr = (0.3, 0.3, 0.5); txt = "控爆炸弹"
        elif powerupType == 'towerFighter': tex = factory.texTowerFighter; cr = (0.5, 0, 0); txt = "防御塔"
        elif powerupType == 'splitBombs': tex = factory.texSplitBombs; cr = (0.8, 0.8, 0.1); txt = "分裂炸弹"
        elif powerupType == 'clusterBombs': tex = factory.texClusterBombs; cr = (0, 0.1, 0.8); txt = "多重炸弹"
        elif powerupType == 'snowball': tex = factory.texSno; cr = (0.9, 0.9, 0.9); txt = "雪球"
        elif powerupType == 'drone': tex = factory.texDrone; cr = (0.9, 0.1, 0.1); txt = "无人机"
        elif powerupType == 'bot': tex = factory.texBot; cr = (0.9, 0.1, 0.9); txt = "机器人"
        else: raise Exception("invalid powerupType: "+str(powerupType))

        if len(position) != 3: raise Exception("expected 3 floats for position")
        


        if powerupType not in ('drone', 'bot', 'snowball'):  
            self.node = bs.newNode(
                'prop',
                delegate=self,
                attrs={'body':'box',
                    'position':position,
                    'model':factory.model,
                    'lightModel':factory.modelSimple,
                    'shadowSize':0.5,
                    'colorTexture':tex,
                    'reflection':'powerup',
                    'reflectionScale':[1.0],
                    'materials':(factory.powerupMaterial,
                                    bs.getSharedObject('objectMaterial'))})
        elif powerupType == 'drone':
            self.node = bs.newNode(
                'prop',
                delegate=self,
                attrs={'body':'box',
                    'position':position,
                    'model':factory.droneModel,
                    'shadowSize':0.5,
                    'colorTexture':tex,
                    'reflection':'powerup',
                    'reflectionScale':[1.0],
                    'materials':(factory.powerupMaterial,
                                    bs.getSharedObject('objectMaterial'))})

        elif powerupType == 'bot':
            self.node = bs.newNode(
                'prop',
                delegate=self,
                attrs={'body':'capsule',
                    'position':position,
                    'model':factory.botModel,
                    'shadowSize':0.5,
                    'colorTexture':tex,
                    'reflection':'powerup',
                    'reflectionScale':[1.0],
                    'materials':(factory.powerupMaterial,
                                    bs.getSharedObject('objectMaterial'))})
        
        elif powerupType == 'snowball':
            self.node = bs.newNode(
                'prop',
                delegate=self,
                attrs={'body':'box',
                    'position':position,
                    'model':factory.snoModel,
                    'lightModel':factory.modelSimple,
                    'shadowSize':0.5,
                    'colorTexture':tex,
                    'reflection':'powerup',
                    'reflectionScale':[1.0],
                    'materials':(factory.powerupMaterial,
                                    bs.getSharedObject('objectMaterial'))})

        # animate in..
        if powerupType not in ('drone', 'bot'):
            curve = bs.animate(self.node,"modelScale",{0:0,140:1.6,200:1})
            bs.gameTimer(200,curve.delete)
        elif powerupType == 'drone':
            curve = bs.animate(self.node,"modelScale",{0:0,140:0.35,200:0.3})
            bs.gameTimer(200,curve.delete)
        elif powerupType == 'bot':
            curve = bs.animate(self.node,"modelScale",{0:0,140:0.8,200:0.7})
            bs.gameTimer(200,curve.delete)

        import anm 
        anm.power_up_modif.pwm(self=self, color=cr, text=txt)

        if expire:
            bs.gameTimer(defaultPowerupInterval-2500,
                         bs.WeakCall(self._startFlashing))
            bs.gameTimer(defaultPowerupInterval-1000,
                         bs.WeakCall(self.handleMessage, bs.DieMessage()))

    @classmethod
    def getFactory(cls):
        """
        Returns a shared bs.PowerupFactory object, creating it if necessary.
        """
        activity = bs.getActivity()
        if activity is None: raise Exception("no current activity")
        try: return activity._sharedPowerupFactory
        except Exception:
            f = activity._sharedPowerupFactory = PowerupFactory()
            return f
            
    def _startFlashing(self):
        if self.node.exists(): self.node.flashing = True

        
    def handleMessage(self, msg):
        self._handleMessageSanityCheck()

        if isinstance(msg, PowerupAcceptMessage):
            factory = self.getFactory()
            if self.powerupType == 'health':
                bs.playSound(factory.healthPowerupSound, 3,
                             position=self.node.position)
            bs.playSound(factory.powerupSound, 3, position=self.node.position)
            self._powersGiven = True
            self.handleMessage(bs.DieMessage())

        elif isinstance(msg, _TouchedMessage):
            if not self._powersGiven:
                node = bs.getCollisionInfo("opposingNode")
                if node is not None and node.exists():
                    # We won't tell the spaz about the bunny.  It'll just happen.
                    if self.powerupType == 'bot':
                        try:
                            p = node.getDelegate().getPlayer()
                            if not 'bunnies' in p.gameData:
                                import BuddyBunny
                                p.gameData['bunnies'] = BuddyBunny.BunnyBotSet(p)
                            p.gameData['bunnies'].doBunny()
                            self._powersGiven = True
                            self.handleMessage(bs.DieMessage())
                        except:
                            pass
                    #a Spaz doesn't know what to do with a snoball powerup. All the snowball functionality
                    #is handled through SnoBallz.py to minimize modifications to the original game files
                    elif self.powerupType == 'snowball':
                        spaz = node.getDelegate()
                        import SnoBallz
                        SnoBallz.snoBall().getFactory().giveBallz(spaz)
                        self._powersGiven = True
                        self.handleMessage(bs.DieMessage())
                    else:
                        node.handleMessage(PowerupMessage(self.powerupType,sourceNode=self.node))

        elif isinstance(msg, bs.DieMessage):
            if self.node.exists():
                if (msg.immediate):
                    self.node.delete()
                else:
                    if self.powerupType == 'drone':
                        curve = bs.animate(self.node, "modelScale", {0:0.3,100:0})
                    elif self.powerupType == 'bot':
                        curve = bs.animate(self.node, "modelScale", {0:0.7,100:0})
                    else:
                        curve = bs.animate(self.node, "modelScale", {0:1,100:0})
                    bs.gameTimer(100,self.node.delete)

        elif isinstance(msg ,bs.OutOfBoundsMessage):
            self.handleMessage(bs.DieMessage())

        elif isinstance(msg, bs.HitMessage):
            self.node.handleMessage("impulse", 
                msg.pos[0],msg.pos[1],msg.pos[2],
                msg.velocity[0],msg.velocity[1],msg.velocity[2],
                msg.magnitude, msg.velocityMagnitude, msg.radius, 
                0, msg.forceDirection[0],msg.forceDirection[1],
                msg.forceDirection[2])

        else:
            bs.Actor.handleMessage(self, msg)
